package com.ad2campaign.engine.core.service;

import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobOperator;

import com.ad2campaign.engine.core.EngineActions;
import com.ad2campaign.engine.core.JobInstanceComparator;
import com.ad2campaign.engine.core.TaskStatus;
import com.ad2campaign.engine.db.dao.PushNotificationMasterDao;
import com.ad2campaign.engine.db.model.Node;
import com.ad2campaign.engine.rest.Loadbalancer;
import com.ad2campaign.engine.rest.RestActionResponse;

public class GenericJobServiceImpl implements GenericJobService
{
	private static final Logger LOGGER = Logger.getLogger(GenericJobServiceImpl.class);
	private JobOperator jobOperator;
	private JobExplorer jobExplorer;
	private PushNotificationMasterDao pushNotificationMasterDao;
	private Loadbalancer loadbalancer;


@Override
	public void resumeJobs(Set<Node> exclude,String jobName)
	{
 		RestActionResponse response =  stopped(jobName);
		Map<String,String> destroyMap = new HashMap<String, String>();
		destroyMap.put("jobName", jobName);
		List<?> executions=response.getData();
		for (Object executionId:executions)
		{
			destroyMap.put("executionId", String.valueOf(executionId));
			LOGGER.info(String.format("resumeJobs():Staring job of execution id [%d]..", executionId));
			TaskStatus taskStatus = pushNotificationMasterDao.findTaskStatus((Long)executionId);
 			if (taskStatus!=TaskStatus.PAUSED)
			{
				RestActionResponse loadBalancerResponse= loadbalancer.process(EngineActions.RESTART.getAction(), null, destroyMap,exclude );
 				LOGGER.info(String.format("resumeJobs():Response for job of execution id [%d] is %s", executionId,loadBalancerResponse));

			}
		}

	}
	
	
	@Override
  public RestActionResponse startJob( String jobName,  String jobParam)
	{
		

		RestActionResponse response = new RestActionResponse();
		try 
		{
			String parameter = URLDecoder.decode(jobParam, "UTF-8");
		 	LOGGER.info(String.format("Starting job [%s] with parameter [%s]",jobName,jobParam));
		 	response.setStatus(true);
		 	Long executionId=jobOperator.start(jobName, parameter);
 		 	response.setData(Arrays.asList(String.valueOf(executionId)));
 		 	response.setResponseMessage(String.format("Job started successfully with result code [%s]", String.valueOf(executionId)));
  		}
		 catch (Exception e)
		{
			LOGGER.error("GenericJobServiceImpl.startJob() : "+e.getMessage());
			LOGGER.debug(e);
			response.setStatus(false);
			response.setResponseMessage(e.getMessage());
  		}
		return response; 
 	}
	
	@Override
   public RestActionResponse stopJob( Long executionId, String jobName)
	{		
		RestActionResponse response = new RestActionResponse();
 	  	try 
		{
		 	LOGGER.info(String.format("Stopping job [%s] of execution ID [s]",jobName,String.valueOf(executionId)));
		 	response.setStatus(jobOperator.stop(executionId));
		 	pushNotificationMasterDao.updateMessageStatusAndExecutionIdByExecutionId(executionId, executionId, TaskStatus.PAUSED.getStatusValue());
 		 	response.setResponseMessage("Stop message was successfully sent (does not guarantee that the job has stopped)");
 		}
  		catch (Exception e) 
		{
			LOGGER.error("GenericJobServiceImpl.stopJob("+executionId+")"+e.getMessage());
			LOGGER.debug(e);
			response.setStatus(false);
			response.setResponseMessage(e.getMessage());
		}
		return response; 
 	}

	@Override
  public RestActionResponse stopAllJob( String jobName)
	{
		RestActionResponse response = new RestActionResponse();
 	 	Set<Long> executions=null;
		try 
		{
			executions = jobOperator.getRunningExecutions(jobName);
		}
		catch (Exception e) 
		{
			LOGGER.error("GenericJobServiceImpl.stopAllJob()"+e.getMessage());
			LOGGER.debug(e);
			response.setStatus(false);
			response.setResponseMessage(e.getMessage());
 		}

		if (executions!=null)
		{
			boolean status=true;
			String errorMessage="";
			LOGGER.info("GenericJobServiceImpl.stopAllJob(): Found ["+executions.size()+"] running jobs.");
	 		for (Long executionId :executions)
			{
				LOGGER.info("GenericJobServiceImpl.stopAllJob(): Attempting now to stop them");
 	 			try
				{
 	 				status = jobOperator.stop(executionId) && status;
				} 
			 	catch (Exception e) 
				{
					LOGGER.error("GenericJobServiceImpl.stopAllJob()"+e.getMessage());
					LOGGER.debug(e);
					errorMessage+= String.format("For executionId [%s] Error is [%s] ",String.valueOf(executionId),e.getMessage()) + "|";
				}  
 			}
	 		response.setStatus(status);
	 		response.setResponseMessage(errorMessage);
		}
		else
		{
			response.setStatus(false);
			response.setResponseMessage("No running execution found");
 		}
		return response;	
	
 	}

  
	 
	
	@Override
  public RestActionResponse restart( String jobName, Long executionId)
	{
		RestActionResponse response = new RestActionResponse();
 		try 
		{
		 	LOGGER.info(String.format("Restarting job [%s] of execution ID [s]",jobName,String.valueOf(executionId)));
		 	Long newExecutionId= jobOperator.restart(executionId);
		 	
		 	pushNotificationMasterDao.updateMessageStatusAndExecutionIdByExecutionId(executionId, newExecutionId, TaskStatus.RUNNING.getStatusValue());
		 	response.setStatus(true);
  		 	response.setData(Arrays.asList(executionId));
 		 	response.setResponseMessage(String.format("Job re-started successfully with new execution id [%s]", String.valueOf(newExecutionId)));
 		}  
		catch (Exception e) 
		{
			LOGGER.error(String.format( "GenericJobServiceImpl.restart(%s,%s) : "+e.getMessage(),jobName,String.valueOf(executionId)));
			LOGGER.debug(e);
			response.setStatus(false);
			response.setResponseMessage(e.getMessage());
		}
		return response;
	}
	
 
	@Override
  public RestActionResponse running( String jobName)
	{
		RestActionResponse response = new RestActionResponse();
 	 	Set<Long> executions=null;
		try 
		{
			executions = jobOperator.getRunningExecutions(jobName);
		 	if (executions!=null)
			{
				response.setData( Arrays.asList( executions.toArray()  ) );
		 		response.setResponseMessage(String.format( "Found [%d] running job(s)",executions.size()));	
 			}
		 	else
		 	{
		 		response.setResponseMessage("Found [%d] running job");	
		 	}
		 	response.setStatus(true);
 		}
		catch (Exception e) 
		{
			LOGGER.error(String.format("GenericJobServiceImpl.running(%s)"+e.getMessage(),jobName));
			LOGGER.debug(e);
			response.setStatus(false);
			response.setResponseMessage(e.getMessage());
 		}
 		return response;	
	
 	}

  
	
@Override	 
 public RestActionResponse stopped( String jobName)
	{
 		return getExecutionIdByStatus(jobName, BatchStatus.STOPPED);
 	}
	
	 @Override
 public RestActionResponse stopping( String jobName)
	{
 		return getExecutionIdByStatus(jobName, BatchStatus.STOPPING);
 	}
	
	 @Override
 public RestActionResponse starting( String jobName)
	{
 		return getExecutionIdByStatus(jobName, BatchStatus.STARTING);
 	}

  @Override
 public RestActionResponse completed( String jobName)
	{
 		return getExecutionIdByStatus(jobName, BatchStatus.COMPLETED);
 	}
@Override
  public RestActionResponse failed( String jobName)
	{
 		return getExecutionIdByStatus(jobName, BatchStatus.FAILED);
 	}
 


	
	
JobInstanceComparator jobInstanceComparator = new JobInstanceComparator();
	private RestActionResponse getExecutionIdByStatus( String jobName,BatchStatus batchStatus)
	{
		RestActionResponse response = new RestActionResponse();
 	 	List<JobInstance> executions=null;
		try 
		{
			executions = jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE);
		 	if (executions!=null)
			{
		 		List<Long> filteredJobs= new LinkedList<Long>();
		 		for (JobInstance jobInstance : executions)
		 		{
		 			
  		 			List<JobExecution> jobExecutions = jobExplorer.getJobExecutions(jobInstance);
  		 	 		Collections.sort(jobExecutions, jobInstanceComparator );
  		 			
  		 			
  		 			if (jobExecutions!=null && jobExecutions.size()>0)
 		 			{
  		 					JobExecution lastJobExecution = jobExecutions.get(0);
   		 					if (lastJobExecution.getStatus() == batchStatus)
 				 			{
 		 						if (!filteredJobs.contains(lastJobExecution.getId()))
 		 						filteredJobs.add(lastJobExecution.getId());
 				 		 	}
  		 			}
 		 			
		 			
		 		}
				response.setData( filteredJobs );
 				response.setResponseMessage(String.format( "Found [%d] %s job(s)",filteredJobs.size(),batchStatus.toString()));	
 				response.setStatus(true);
  			}
		 	else
		 	{
		 		response.setResponseMessage("Found [0] "+batchStatus.toString()+" job");
		 		response.setStatus(false);
		 	}
 		}
		catch (Exception e) 
		{
			LOGGER.error(String.format("GenericJobServiceImpl.%s(%s)"+e.getMessage(),batchStatus.toString(),jobName));
			LOGGER.debug(e);
			response.setStatus(false);
			response.setResponseMessage(e.getMessage());
 		}
 		return response;	
	
 	}

	public JobOperator getJobOperator() {
		return jobOperator;
	}

	public void setJobOperator(JobOperator jobOperator) {
		this.jobOperator = jobOperator;
	}

	public JobExplorer getJobExplorer() {
		return jobExplorer;
	}

	public void setJobExplorer(JobExplorer jobExplorer) {
		this.jobExplorer = jobExplorer;
	}

	public PushNotificationMasterDao getPushNotificationMasterDao() {
		return pushNotificationMasterDao;
	}

	public void setPushNotificationMasterDao(
			PushNotificationMasterDao pushNotificationMasterDao) {
		this.pushNotificationMasterDao = pushNotificationMasterDao;
	}

	public Loadbalancer getLoadbalancer() {
		return loadbalancer;
	}


	public void setLoadbalancer(Loadbalancer loadbalancer) {
		this.loadbalancer = loadbalancer;
	}

}
